package demo.designPattern.proxy.cglib;

import demo.spring.service.WelcomeService;
import demo.vo.People;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.junit.Test;

import java.lang.reflect.Method;
import java.time.LocalTime;

/**
 * CGLIB（Code Generation Library）是一个功能强大的库，用于在运行时动态生成类。
 * 与 Java 的内置动态代理不同，CGLIB 可以对常规类（而不仅仅是接口）进行代理，通过生成目标类的子类来实现。
 * <p>
 * CGLIB 动态代理的优势在于能够对类进行代理，而不仅仅是接口。
 * 使用 CGLIB 时，生成的代理类是目标类的子类，因此可以拦截并增强目标类的方法。
 * CGLIB 在性能上相对优于 Java 的反射机制，适合需要高性能的场景。
 * 但请注意，CGLIB 不能代理 final 类和 final 方法，因为它不能被子类化。
 * <p>
 * CGLIB 通常适用于以下场景：
 * <li>高性能需求：在一些需要频繁调用的场景中，CGLIB 可以提供比 Java 动态代理更好的性能。
 * <li>复杂业务逻辑：当需要代理的类的方法较多或者需要添加逻辑时，CGLIB 提供了较为灵活的解决方案。
 * <li>无需接口的情况：当目标类没有实现接口时，CGLIB 可以轻松代理这些类。
 * <p>
 * 注意事项
 * <li>CGLIB 生成的代理类依赖于继承，因此目标类不能是 final。
 * <li>CGLIB 对于性能的影响通常比反射要小，但在大规模应用中，仍需注意性能。
 * <li>CGLIB 需要在运行时生成字节码，因此会消耗一定的内存和CPU资源。
 */
public class CglibProxyDemo {

    /**
     * CGLIB 动态代理的实现步骤
     * <li>添加 CGLIB 依赖
     * <li>创建目标类：定义一个要被代理的类，并实现所需的方法。
     * <li>创建方法拦截器：实现 CGLIB 的 MethodInterceptor 接口，该接口中的 intercept 方法会在调用目标方法时被触发。
     * <li>创建代理对象：通过 Enhancer 类动态生成目标类的代理对象。
     */
    @Test
    public void demo1() {
        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();
        // 设置目标类
        enhancer.setSuperclass(WelcomeService.class);
        // 设置方法拦截器
        enhancer.setCallback(new CglibMethodInterceptorDemo());
        // 创建代理对象
        WelcomeService proxy = (WelcomeService) enhancer.create();
        // 通过代理对象调用方法
        String a = proxy.welcome("cglib");
        System.out.println(a);
    }


    /**
     * CGLIB动态字节码生成。使用动态字节码生成技术实现AOP原理是在运行期间目标字节码加载后，生成目标类的子类，将切面逻辑加入到子类中，所以cglib实现AOP不需要基于接口。
     * CGLIB是一个强大的、高性能的Code生成类库，它可以在运行期间扩展Java类和实现Java接口，它封装了Asm，所以使用cglib前需要引入Asm的jar
     */
    @Test
    public void proxyByCGlib() {
        People people = new People();
        people.setId(0);
        people.setName("Stan");

        CGlibProxyFactory factory = new CGlibProxyFactory();

        People service = (People) factory.createProxyInstance(people);

        service.say("888");

    }
}


class CglibMethodInterceptorDemo implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 在目标方法执行之前
        beforeInvoke();

        // 调用目标方法
        Object result = proxy.invokeSuper(obj, args);

        // 在目标方法执行之后
        afterInvoke();

        return result;
    }


    // 记录方法调用时间
    public void beforeInvoke() {
        System.out.println("调用开始时间：" + LocalTime.now());
    }

    public void afterInvoke() {
        System.out.println("调用结束时间：" + LocalTime.now());
    }
}